package hku.cs.lunuo.instrument;

import hku.cs.lunuo.constant.DirAndFileConstant;
import hku.cs.lunuo.tools.DirTools;
import hku.cs.lunuo.tools.TextFileNameFilter;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.output.XMLOutputter;

class InEs {
	Integer i;
	String className;
	String methodName;

	public InEs(String className, String methodName, Integer i) {
		super();
		this.i = i;
		this.className = className;
		this.methodName = methodName;
	}
	
	public InEs() {
		
	}

}
public class Counter {
	
	public static InEs lastBlk = new InEs();
	
	public static HashMap<String, HashMap<String, HashMap<Integer, Integer>>> counter = new HashMap<String, HashMap<String, HashMap<Integer, Integer>>>();

	public static HashMap<String, HashMap<String, HashMap<String, Integer>>> escounter = new HashMap<String, HashMap<String, HashMap<String, Integer>>>();

	private static List<InEs> tempES = new ArrayList<InEs>();

	public static synchronized void increase(String className,
			String methodName, int blkIdx) {
//		if(className.equals("org.apache.tools.ant.util.CollectionUtils")&&methodName.equals("boolean equals(java.util.Dictionary,java.util.Dictionary)"))
//		System.out.println(blkIdx);
		HashMap<String, HashMap<Integer, Integer>> metCnt = counter
				.get(className);
		if (metCnt == null)
			metCnt = new HashMap<String, HashMap<Integer, Integer>>();

		HashMap<Integer, Integer> al = metCnt.get(methodName);

		if (al == null)
			al = new HashMap<Integer, Integer>();

		if (null == al.get(blkIdx)) {
			al.put(blkIdx, 1);
		} else {
			int tmp = al.get(blkIdx);
			tmp++;
			al.put(blkIdx, tmp);
		}

		metCnt.put(methodName, al);
		counter.put(className, metCnt);
	}
	
	public static synchronized void recordLast(String className, String methodName, int s) {
		lastBlk = new InEs(className, methodName, s);
//		if(lastBlk.className.contains("AbstractTokenizer")&&lastBlk.methodName.contains("completeWhitespace")) {
//			
//			System.out.println(lastBlk.i);
////			try {
////					Thread.sleep(10000);
////			} catch (InterruptedException e1) {
////				// TODO Auto-generated catch block
////				e1.printStackTrace();
////			}
//		}
	}
	
	public static synchronized void confirmLast() {
		increaseES(lastBlk.className, lastBlk.methodName, lastBlk.i);
//		if(lastBlk.className.contains("AbstractTokenizer")&&lastBlk.methodName.contains("completeWhitespace")) {
//			try {
//				System.out.println(lastBlk.i);
//				Thread.sleep(10000);
//			} catch (InterruptedException e1) {
//				// TODO Auto-generated catch block
//				e1.printStackTrace();
//			}
//		}
//		tempES.add(lastBlk);
	}
	
	public static synchronized void flushES(String className, String methodName, String s,
			int e, int current) {
		
		if (tempES.size() == 0)
			return;
		
//		if(className.contains("AbstractTokenizer")&&methodName.contains("completeWhitespace")) {
//			for (int i = tempES.size() - 1; i >= 0; i--) {
//				System.out.println(tempES.get(i).i + " "+className+" "+methodName);
//			}
//			System.out.println();
//			try {
//				Thread.sleep(10000);
//			} catch (InterruptedException e1) {
//				// TODO Auto-generated catch block
//				e1.printStackTrace();
//			}
//			
//			System.out.println();
//		}

		String es = "";
		boolean isC = false;
		
		String[] xxxx = s.split("_");
//		System.out.println(className+" "+methodName+" starts"+s);
		Set<Integer> sxx = new HashSet<Integer>();
		for(String x : xxxx)
			sxx.add(Integer.parseInt(x));

		int z = 0;
		for (int i = tempES.size() - 1; i >= 0; i--) {
			InEs ines = tempES.get(i);
			if (ines.className.equals(className)
					&& ines.methodName.equals(methodName)) {
				if (es.length() == 0 && ines.i != e) {
//					if(methodName.equals("boolean isSeparator(int)"))
//						System.out.println(ines.i);
					return;
				}
				
				String tt = ines.i.toString() + ">";
				es = tt + es;
				tempES.remove(i);
				z++;
				
				
				//-----------------
				if(xxxx.length>1) {
					if (sxx.contains(ines.i)&&z>1) {
						isC = true;
						break;
					}
				}else
					if (sxx.contains(ines.i)) {
						isC = true;
						break;
					}
				
				//----actually, it is better to know clearly the pred chain 
				//from which node to end(know we only know the node before the 
				//pred chain start node.
				
				//we fix this bug because "while" will cause a problem
//				 code class SpecialSequencesIterator method hasNext()
//				 lock 6:[preds: 5] [succs: 7]
//               1619: l2 = virtualinvoke l1.<de.susebox.java.util.SortedArray: int size()>()
//
//               Block 7:[preds: 6 8] [succs: 8 10]
//               1621: $i0 = l0.<de.susebox.java.util.SpecialSequencesIterator: int _currentIndex>
//               1621: $i1 = $i0 + 1
//               1621: l0.<de.susebox.java.util.SpecialSequencesIterator: int _currentIndex> = $i1
//               1621: if $i1 >= l2 goto $r4 = l0.<de.susebox.java.util.SpecialSequencesIterator: de.susebox.java.util.SortedArray[] _arrays>
//
//               Block 8:[preds: 7] [succs: 9 7]
//               1622: $i1 = l0.<de.susebox.java.util.SpecialSequencesIterator: int _currentIndex>
//               1622: $r3 = virtualinvoke l1.<de.susebox.java.util.SortedArray: de.susebox.java.util.SpecialSequence get(int)>($i1)
//               1622: l0.<de.susebox.java.util.SpecialSequencesIterator: de.susebox.java.util.SpecialSequence _currentElem> = $r3
//               1623: $z0 = specialinvoke l0.<de.susebox.java.util.SpecialSequencesIterator: boolean listHasNext()>()
//               1623: if $z0 == 0 goto $i0 = l0.<de.susebox.java.util.SpecialSequencesIterator: int _currentIndex>
//
//               Block 9:[preds: 8] [succs:]
//               1624: return 1
//	here right chain will be "6->7->8-9" or others, which pred chain start from node 7
//	and the prev nodes are (6_8), but when search from ending, it will cut chain to 
//	to 8->9. it misunderstand node 8.
//	here we fix it. fisrt this will only happend as while grammer. normal pred chain hasn't this exception.
//	because the prev nodes of start node of pred chain will lesser, which will not mislead the
//	"flushES" at the exit node of pred chain use "startIS"'s large node to cut.
//	if cutting has happend, the es now will always not larger than 1, because the error will
//	only happen when the one of prev nodes equals to end node of pred chain.
				
				
			}
			
			//else  //when muti thread, the last one may be not 
			//return
		}
		if(es.length()==0) 
			return; //all ones in tempES is not in same klass and method
		
		if (!isC && es.length() != 0) {
//			System.out.println(es + " " + s);
			return;
//			throw new IllegalStateException("Lunuo Exception : Wrong evaluate sequences "+ className+" "+methodName + " "+es+" "+s );
		}
//		}
		
//		es += current+">";
//		if(es.length()>=1)
//			es = es.substring(0, es.length() - 1);
//		System.out.println(es+" is right"+s+ className+" "+methodName);
		es+=current;
		if(xxxx.length==1)
			es="-1>"+es;
		
//		if(className.contains("SpecialSequencesIterator")&&methodName.contains("hasNext")) {
//			System.out.println(es);	
//		}
		
		add(className, methodName, es);
		
	}


//	public static synchronized void flushES(String className, String methodName, int s,
//			int e, int status) {
//		// tempES.clear();
//		// return;
//
//		if (tempES.size() == 0)
//			return;
//
//		String es = "";
//		boolean isC = false;
//
//		for (int i = tempES.size() - 1; i >= 0; i--) {
//			InEs ines = tempES.get(i);
//			if (ines.className.equals(className)
//					&& ines.methodName.equals(methodName)) {
//				if (es.length() == 0 && ines.i != e) {
//					return;
//				}
//				String tt = ines.i.toString() + ">";
//				es = tt + es;
//				tempES.remove(i);
//				if (ines.i == s) {
//					isC = true;
//					break;
//				}
//			}
//		}
//		
//		if (!isC && es.length() != 0) {
//			System.out.println(es + " " + s);
//			throw new IllegalStateException("Lunuo Exception : Wrong evaluate sequences ");
//		}
//		if(es.length()==0)
//			return;
//		if (status == 0)
//			es += "-1>";
//		if(es.length()>=1)
//			es = es.substring(0, es.length() - 1);
//		add(className, methodName, es);
//		
//	}
	

	public static synchronized void add(String className, String methodName,
			String es) {
		if (es.length() >= 1) {
//			if(className.equals("de.susebox.java.io.ExtIOException"))
////					&&methodName.equals("java.lang.String getMessage()"))
//				System.out.println(methodName+" "+es);
			HashMap<String, HashMap<String, Integer>> inEscounter;
			HashMap<String, Integer> in2Escounter;
			Integer icnt;
			if ((inEscounter = escounter.get(className)) == null) {
				inEscounter = new HashMap<String, HashMap<String, Integer>>();
			}
			if ((in2Escounter = inEscounter.get(methodName)) == null) {
				in2Escounter = new HashMap<String, Integer>();
			}

			if ((icnt = in2Escounter.get(es)) == null) {
				in2Escounter.put(es, 1);
			} else
				in2Escounter.put(es, icnt + 1);

			inEscounter.put(methodName, in2Escounter);
			escounter.put(className, inEscounter);
		}
	}

	public static synchronized void increaseES(String className,
			String methodName, int blkIdx) {
		tempES.add(new InEs(className, methodName, blkIdx));
	}
	

	public static synchronized void reportES() {

		for (String klassNameKey : escounter.keySet()) {
			HashMap<String, HashMap<String, Integer>> inEscounter = escounter
					.get(klassNameKey);
			for (String methodNameKey : inEscounter.keySet()) {
				HashMap<String, Integer> in2Escounter = inEscounter
						.get(methodNameKey);
				for (String esKey : in2Escounter.keySet()) {
					System.out.println(klassNameKey + "->" + methodNameKey
							+ "->" + esKey + " = " + in2Escounter.get(esKey));
				}
			}

		}
	}

	public static synchronized void report(String path, String klassName,
			String testName) {
//		if (tempES.size() > 0) {
//			String message = "";
//			for (int i = 0; i < tempES.size(); i++) {
//				InEs iss = tempES.get(i);
//				message += iss.i + " " + iss.className + " " + iss.methodName
//						+ "\n";
//			}
//			tempES.clear();
//			throw new IllegalStateException(
//					"Lunuo Exception : Status Wrong" + klassName+"\n"
//							+ " " + message);
//			
//
//		}

		String sep = File.separator;
		StringBuffer sb = new StringBuffer();
		sb.append(path);
		sb.append(sep);
		String k[] = klassName.split(" ");
		if (k.length == 2)
			klassName = k[1];
		sb.append(klassName);
		sb.append(sep);

		File xxf = new File(sb.toString());
		DirTools.prepare(sb.toString());
		String[] xxfs = xxf.list(new TextFileNameFilter(testName));
		if (xxfs.length == 1) {
			testName += ".1";
		} else if (xxfs.length > 1)
			testName += "." + xxfs.length;

		sb.append(testName);
		sb.append(sep);
		File f = DirTools.prepare(sb.toString() + DirAndFileConstant.BLOCK);

		for (String className : counter.keySet()) {
			HashMap<String, HashMap<Integer, Integer>> metCnt = counter
					.get(className);

			Element xmlRoot = new Element("class");
			xmlRoot.setAttribute("name", className);
			Document xmlDoc = new Document(xmlRoot);

			for (String methodName : metCnt.keySet()) {
				Element xmlMethod = new Element("method");
				xmlMethod.setAttribute("name", methodName);

				HashMap<Integer, Integer> cnt = metCnt.get(methodName);
				for (Iterator<Integer> i = cnt.keySet().iterator(); i.hasNext();) {
					Integer idx = i.next();
					Element xmlBlock = new Element("block");
					xmlBlock.setAttribute("index", idx.toString());
					xmlBlock.setAttribute("times", cnt.get(idx).toString());

					xmlMethod.addContent(xmlBlock);
				}
				xmlRoot.addContent(xmlMethod);
			}

			try {
				new XMLOutputter().output(xmlDoc, new FileOutputStream(
						new File(f, className + ".xml")));
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		counter.clear();

		f = DirTools.prepare(sb.toString() + DirAndFileConstant.PRED);

		for (String className : escounter.keySet()) {
			HashMap<String, HashMap<String, Integer>> metCnt = escounter
					.get(className);

			Element xmlRoot = new Element("class");
			xmlRoot.setAttribute("name", className);
			Document xmlDoc = new Document(xmlRoot);

			for (Iterator<String> methodIt = metCnt.keySet().iterator(); methodIt
					.hasNext();) {
				String methodName = methodIt.next();
				Element xmlMethod = new Element("method");
				xmlMethod.setAttribute("name", methodName);

				HashMap<String, Integer> cnt = metCnt.get(methodName);
				for (Iterator<String> i = cnt.keySet().iterator(); i.hasNext();) {
					String idx = i.next();
					Element xmlBlock = new Element("es");
					xmlBlock.setAttribute("sequence", idx);
					xmlBlock.setAttribute("times", cnt.get(idx).toString());

					xmlMethod.addContent(xmlBlock);
				}
				xmlRoot.addContent(xmlMethod);
			}

			try {
				new XMLOutputter().output(xmlDoc, new FileOutputStream(
						new File(f, className + ".xml")));
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		escounter.clear();

	}
}
